/*
The MIT License (MIT)

Copyright (c) 2013 Mike Dapiran, Brian May, Richard Pospesel, and Bert Wierenga

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software 
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "String.h"

#include "Float.h"
#include "Double.h"

#include "../Hogshead.Common.IO.h"

#include "../DataStructures/Array1D.h"
#include "../DataStructures/IList.h"
#include "../DataStructures/Pair.h"

#include <cstring>
#include <cstdio>

//#include "tinyxml.h"
#include "../Utils/StringBuilder.h"
#include "../DataStructures/ArrayList.h"
// gets rid of the sprintf warnings, there won't be any problems with that here
#pragma warning( push )
#pragma warning(disable : 4996)



namespace Hogshead
{
	namespace Common
	{

		String::String()
		{
			_length = 0;
			_string_data = new char[1];
			_string_data[0] = NULL;
		}
		String::~String()
		{
			delete[] _string_data;
		}
			
		String::String(const String& in_other)
		{
			_length = in_other._length;
			_string_data = new char[_length + 1];
			if(_length != 0) memcpy(_string_data, in_other._string_data, _length);
			_string_data[_length] = 0;
			
		}
		String::String(const char* in_char_array)
		{
			if(in_char_array == NULL)
			{
				_length = 0;
				_string_data = new char[1];
				_string_data[0] = NULL;
			}
			else
			{
				_length = strlen(in_char_array);
				_string_data = new char[_length + 1];
				if(_length != 0) memcpy(_string_data, in_char_array, _length);
				_string_data[_length] = 0;
			}
		}
			
		String::String(const char* in_char_array, int in_offset, int in_length)
		{
			_length = in_length;
			_string_data = new char[_length + 1];
			char* other = (char*)(in_char_array +in_offset);
			memcpy(_string_data, other, _length);
			_string_data[_length] = 0;
		}

		String::String(char in_char)
		{
			_length = 1;
			_string_data = new char[2];
			_string_data[0] = in_char;
			_string_data[1] = 0;
		}

		String::String(unsigned char in_char)
		{
			_length = 1;
			_string_data = new char[2];
			_string_data[0] = (char)in_char;
			_string_data[1] = 0;
		}

		String::String(bool in_bool)
		{
			if(in_bool)
			{
				_length = 4;
				_string_data = new char[5];
				_string_data[0] = 'T';
				_string_data[1] = 'r';
				_string_data[2] = 'u';
				_string_data[3] = 'e';
				_string_data[4] = 0;
			}
			else
			{
				_length = 5;
				_string_data = new char[6];
				_string_data[0] = 'F';
				_string_data[1] = 'a';
				_string_data[2] = 'l';
				_string_data[3] = 's';
				_string_data[4] = 'e';
				_string_data[5] = 0;
			}
		}

		String::String(int in_int)
		{
			// longest int: -2147483648; 10 digits + sign + 0 == 12
			_string_data = new char[12];
			_length = sprintf(_string_data, "%d", in_int);
		}
		String::String(unsigned int in_uint)
		{
			// longest int: 4294967295; 10 digits + 0 == 11
			_string_data = new char[11];
			_length = sprintf(_string_data, "%u", in_uint);
		}
		String::String(long long in_long_long)
		{
			// longest long long: -9.22337204 × 10^18; 18 digits + sign + 0 == 20
			_string_data = new char[20];
			_length = sprintf(_string_data, "%I64d", in_long_long);
		}
		String::String(unsigned long long in_ulong_long)
		{
			// longest unsigned long long: 1.84467441 × 10^19; 19 digits + 0 == 20
			_string_data = new char[20];
			_length = sprintf(_string_data, "%I64u", in_ulong_long);
		}
		String::String(float in_float)
		{
			if(in_float != in_float)	// nan
			{
				_string_data = new char[4];
				_string_data[0] = 'N';
				_string_data[1] = 'a';
				_string_data[2] = 'N';
				_string_data[3] = 0;
				_length = 3;
				return;
			}
			else if(in_float == Float::Infinity)
			{
				_string_data = new char[9];
				_string_data[0] = 'I';
				_string_data[1] = 'n';
				_string_data[2] = 'f';
				_string_data[3] = 'i';
				_string_data[4] = 'n';
				_string_data[5] = 'i';
				_string_data[6] = 't';
				_string_data[7] = 'y';
				_string_data[8] = 0;
				_length = 8;
				return;
			}
			else if(in_float == Float::NegativeInfinity)
			{
				_string_data = new char[10];
				_string_data[0] = '-';
				_string_data[1] = 'I';
				_string_data[2] = 'n';
				_string_data[3] = 'f';
				_string_data[4] = 'i';
				_string_data[5] = 'n';
				_string_data[6] = 'i';
				_string_data[7] = 't';
				_string_data[8] = 'y';
				_string_data[9] = 0;
				_length = 9;
				return;
			}

			// 32 bit floating point supports up to 7 digits of precision, and an exponent from -90 to 90
			// so sign + 7 digits + decimal + e + sign + 2 exponent digits + 0 == 14
			_string_data = new char[14];
			_length = sprintf(_string_data, "%.6g", in_float);
		}

		String::String(double in_double)
		{
			if(in_double != in_double)	// nan
			{
				_string_data = new char[4];
				_string_data[0] = 'N';
				_string_data[1] = 'a';
				_string_data[2] = 'N';
				_string_data[3] = 0;
				_length = 3;
				return;
			}
			else if(in_double == Double::Infinity)
			{
				_string_data = new char[9];
				_string_data[0] = 'I';
				_string_data[1] = 'n';
				_string_data[2] = 'f';
				_string_data[3] = 'i';
				_string_data[4] = 'n';
				_string_data[5] = 'i';
				_string_data[6] = 't';
				_string_data[7] = 'y';
				_string_data[8] = 0;
				_length = 8;
				return;
			}
			else if(in_double == Double::NegativeInfinity)
			{
				_string_data = new char[10];
				_string_data[0] = '-';
				_string_data[1] = 'I';
				_string_data[2] = 'n';
				_string_data[3] = 'f';
				_string_data[4] = 'i';
				_string_data[5] = 'n';
				_string_data[6] = 'i';
				_string_data[7] = 't';
				_string_data[8] = 'y';
				_string_data[9] = 0;
				_length = 9;
				return;
			}

			// 64 bit floating point supports up to 16 digits of precision, and an exponent from -1022 to 1023
			// so sign + 16 digits + decimal + e + sign + 4 exponent digits + 0 == 25
			_string_data = new char[25];
			_length = sprintf(_string_data, "%.15g", in_double);
		}

		String::String(IList<char>& in_chars)
		{
			_length = in_chars.size();
			_string_data = new char[_length+1];
			for(int k = 0; k < _length; k++)
				_string_data[k] = in_chars[k];
			_string_data[_length] = 0;
		}
/*
		String::String(const TiXmlNode& in_node)
		{
			TiXmlPrinter printer;
			printer.SetStreamPrinting();
			in_node.Accept( &printer );
			const char* text = printer.CStr();
			
			_length = strlen(text);
			_string_data = new char[_length + 1];

			memcpy(_string_data, text, _length);
			_string_data[_length] = 0;
		}
*/
		String::String(const void* in_ptr)
		{
			#ifdef _WIN64
			_string_data = new char[19];
			_length = sprintf(_string_data, "0x%016lx", (unsigned long long)in_ptr)
			#else
			_string_data = new char[11];
			_length = sprintf(_string_data, "0x%08lx", (unsigned long)in_ptr);
			#endif
		}

		int String::length() const
		{
			return _length;
		}

		String::operator const char *() const
		{
			return _string_data;
		}

		bool String::equals(const String& in_string) const
		{
			if(this == &in_string)
				return true;

			if(_length != in_string._length)
				return false;


			for(int k = 0; k <  _length; k++)
			{
				if(_string_data[k] - in_string._string_data[k])
					return false;
			}
			return true;
		}

		bool String::equals(const char* left, const char* right)
		{
			if(left == right) return true;

			if(left == NULL || right == NULL)
				return false;

			while(*left && *right) 
			{
				if(*left++ != *right++) return false;
			}
			if(*left || *right) return false;
			return true;
		}

		bool String::operator==(const char* in_string) const
		{
			return String::equals(_string_data, in_string);
		}

		bool String::operator==(const String& in_string) const
		{
			return equals(in_string);
		}

		bool operator==(const char* left, const String& right)
		{
			return String::equals(left, right._string_data);
		}

		bool String::operator!=(const char* right) const
		{
			return String::equals(_string_data, right) == false;
		}

		bool String::operator!=(const String& in_string) const
		{
			if(_length != in_string._length) return true;
			if(this == &in_string) return false;

			return String::equals(_string_data, in_string._string_data) == false;
		}

		bool operator!=(const char* left, const String& right)
		{
			return String::equals(left, right._string_data) == false;
		}

		String& String::operator=(const String& right)
		{
			if(_length != right._length)
			{
				delete[] _string_data;
				_length = right._length;
				_string_data = new char[_length + 1];
				_string_data[_length] = 0;
			}

			memcpy(_string_data, right._string_data, _length);

			return *this;
		}

		String& String::operator=(const char* right)
		{
			if(right == NULL)
			{
				delete[] _string_data;
				_length = 0;
				_string_data = new char[1];
				_string_data[0] = 0;
				return *this;
			}

			int right_length = strlen(right);
			if(_length != right_length)
			{
				delete[] _string_data;
				_length = right_length;
				_string_data = new char[_length + 1];
				_string_data[_length] = 0;
			}

			memcpy(_string_data, right, _length);

			return *this;
		}

		char String::operator[] (int in_index) const
		{
			if(in_index >= 0 && in_index < _length)
				return _string_data[in_index];
			throw Exception("Index out of bounds");
		}

		Array1D<String> String::split(char c) const
		{
			int start_index = 0;
			int substrings = 0;
			for(int k = 0; k < _length; k++)
			{
				if(_string_data[k] == c)
				{
					substrings++;
					start_index = k+1;
				}
			}

			if(start_index < _length)
				substrings++;

			Array1D<String> result(substrings);
			StringBuilder sb;

			int index = 0;
			for(int k = 0; k < _length; k++)
			{
				if(_string_data[k] == c)
				{
					result(index++) = sb;
					sb.reset();
					start_index = k+1;
				}
				else
					sb << _string_data[k];
			}

			if(index < substrings)
			{
				result(index++) = sb;
			}
			return result;
		}

		Array1D<String> String::split(const String& delimit) const
		{
			Vector<Pair<int,int> > substring_list;
			Pair<int, int> p(0, 0);
			for(int k = 0; k < _length; k++)
			{
				// see if we can find our string
				bool found_delimit = true;
				for(int j = 0; j < delimit._length; j++)
				{
					// not a match
					if(k+j == _length || _string_data[k+j] != delimit._string_data[j])
					{
						found_delimit = false;
						p.second++;
						break;
					}
				}

				if(found_delimit)
				{
					if(k > 0)
						substring_list.add(p);
					k += delimit._length;
					p.first = k;
					p.second = 1;
				}
			}

			if(p.first < _length)
			{
				substring_list.add(p);
			}

			Array1D<String> result(substring_list.size());

			for(int k = 0; k < substring_list.size(); k++)
			{
				Pair<int, int>& p = substring_list[k];
				result[k] = String(_string_data, p.first, p.second);
			}

			return result;
		}
	}
}

// reenable the 4996 warning after this
#pragma warning( pop )